package runners

import (
	"context"
	"fmt"
	"net"
	"strings"
	"time"

	actions_module "code.gitea.io/gitea/models/actions"
	docker_module "code.gitea.io/gitea/modules/docker"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

func RegistGlobalRunner(ctx context.Context) error {
	log.Info("获取全局RunnerToken...")
	actionRunnerToken, err := actions_module.NewRunnerToken(ctx, 0, 0)
	if err != nil {
		return fmt.Errorf("获取全局RunnerToken失败:%v", err)
	}
	runnerCount := setting.Runner.Count
	for i := 0; i < runnerCount; i++ {
		err := RegistRunner(ctx, actionRunnerToken.Token)
		if err != nil {
			return fmt.Errorf("注册Runner失败:%v", err)
		}
	}
	return nil
}

func checkK8sIsEnable() bool {
	return setting.K8sConfig.Enable
}

func RegistRunner(ctx context.Context, token string) error {
	log.Info("开始注册Runner...")

	var err error
	if checkK8sIsEnable() {
		err = registK8sRunner(ctx, token)
	} else {
		err = registDockerRunner(ctx, token)
	}
	if err != nil {
		return fmt.Errorf("注册Runner失败:%v", err)
	}
	log.Info("Runner注册成功: %s", token)
	return nil
}

func registDockerRunner(ctx context.Context, token string) error {
	log.Info("开始注册Runner...")
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return err
	}
	defer cli.Close()
	//拉取act_runner镜像
	dockerHost, err := docker_module.GetDockerSocketPath()
	if err != nil {
		return fmt.Errorf("获取docker socket路径失败:%v", err)
	}
	// 拉取镜像
	err = docker_module.PullImage(ctx, cli, dockerHost, setting.Runner.Image)
	if err != nil {
		return fmt.Errorf("拉取act_runner镜像失败:%v", err)
	}
	//获取本机IP
	ips, err := getLocalIP()
	if err != nil {
		return fmt.Errorf("获取本机IP失败:%v", err)
	}
	//获取InstanceUrl
	conntype := strings.Split(setting.AppURL, "://")[0]
	port := setting.HTTPPort
	instanceURL := conntype + "://" + ips[0] + ":" + port
	timestamp := time.Now().Format("20060102150405")
	//Runner配置
	env := []string{
		"GITEA_INSTANCE_URL=" + instanceURL,
		"GITEA_RUNNER_REGISTRATION_TOKEN=" + token,
		"GITEA_RUNNER_NAME=runner-" + timestamp,
	}
	binds := []string{
		"/var/run/docker.sock:/var/run/docker.sock",
	}
	containerName := "runner-" + timestamp
	//创建并启动Runner容器
	err = docker_module.CreateAndStartContainer(ctx, cli, setting.Runner.Image, nil, env, binds, nil, containerName)
	if err != nil {
		return fmt.Errorf("创建并注册Runner失败:%v", err)
	}
	return nil
}

func DeleteRunnerByName(ctx context.Context, runnerName string) error {
	log.Info("开始停止并删除容器: %s", runnerName)
	var err error
	if checkK8sIsEnable() {
		err = deleteK8sRunnerByName(ctx, runnerName)
	} else {
		err = deleteDockerRunnerByName(ctx, runnerName)
	}
	if err != nil {
		return fmt.Errorf("删除Runner失败:%v", err)
	}
	log.Info("Runner删除成功: %s", runnerName)
	return nil
}

func deleteDockerRunnerByName(ctx context.Context, runnerName string) error {
	log.Info("开始停止并删除容器: %s", runnerName)

	// 创建Docker客户端
	cli, err := docker_module.CreateDockerClient(ctx)
	if err != nil {
		return fmt.Errorf("Docker client创建失败:%v", err)
	}
	log.Info("[StopAndRemoveContainer]Docker client创建成功")
	defer cli.Close()
	err = docker_module.DeleteContainer(ctx, cli, runnerName)
	if err != nil {
		return fmt.Errorf("Runner创建失败:%v", err)
	}
	return nil
}

func getLocalIP() ([]string, error) {
	var ips []string
	interfaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	for _, iface := range interfaces {
		if iface.Flags&net.FlagUp == 0 ||
			iface.Flags&net.FlagLoopback != 0 {
			continue
		}

		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}

		// 遍历地址列表
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}

			ip = ip.To4()
			if ip == nil {
				continue // 非IPv4地址
			}

			ips = append(ips, ip.String())
		}
	}
	if len(ips) == 0 {
		return nil, fmt.Errorf("no valid IP address found")
	}
	return ips, nil
}

func getK8sUrlAndToken() (string, string, error) {
	if !checkK8sIsEnable() {
		return "", "", fmt.Errorf("K8s未启用")
	}
	k8sUrl := setting.K8sConfig.Url
	k8sToken := setting.K8sConfig.Token

	if k8sUrl == "" || k8sToken == "" {
		return "", "", fmt.Errorf("K8s配置不完整")
	}
	return k8sUrl, k8sToken, nil
}

func registK8sRunner(ctx context.Context, token string) error {
	log.Info("开始注册Kubernetes Runner: %s", token)
	k8sURL, k8sToken, err := getK8sUrlAndToken()
	if err != nil {
		return fmt.Errorf("获取K8s配置失败: %v", err)
	}

	// 测试连接
	err = testKubernetesConnection(k8sURL, k8sToken)
	if err != nil {
		return fmt.Errorf("Kubernetes连接测试失败: %v", err)
	}

	// 创建K8s客户端
	clientset, err := createKubernetesClient(k8sURL, k8sToken)
	if err != nil {
		return fmt.Errorf("创建Kubernetes客户端失败: %v", err)
	}
	// 获取实例URL
	instanceURL, err := getInstanceURL()
	if err != nil {
		return fmt.Errorf("获取实例URL失败: %v", err)
	}

	// 创建Runner Deployment
	deployment, err := createRunnerDeployment(token, instanceURL)
	if err != nil {
		return fmt.Errorf("创建Runner Deployment配置失败: %v", err)
	}

	// 部署到Kubernetes
	//namespace := setting.K8sConfig.Namespace
	var namespace string
	if namespace == "" {
		namespace = "act-runner"
	}

	_, err = clientset.AppsV1().Deployments(namespace).Create(ctx, deployment, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("在Kubernetes中创建Runner Deployment失败: %v", err)
	}

	log.Info("成功在Kubernetes中创建Runner: %s", deployment.Name)
	return nil
}

func getInstanceURL() (string, error) {
	// 如果AppURL是公网地址，直接使用
	if setting.AppURL != "" &&
		!strings.Contains(setting.AppURL, "127.0.0.1") &&
		!strings.Contains(setting.AppURL, "localhost") {
		log.Info("使用配置的AppURL: %s", setting.AppURL)
		return setting.AppURL, nil
	}

	// 否则构建URL
	ips, err := getLocalIP()
	if err != nil {
		return "", fmt.Errorf("获取本机IP失败: %v", err)
	}

	if len(ips) == 0 {
		return "", fmt.Errorf("没有找到有效的IP地址")
	}

	// 使用第一个IP构建URL
	conntype := "http"
	if strings.Contains(setting.AppURL, "https://") {
		conntype = "https"
	}

	port := setting.HTTPPort
	instanceURL := conntype + "://" + ips[0] + ":" + port

	log.Info("构建的实例URL: %s", instanceURL)
	return instanceURL, nil
}
func deleteK8sRunnerByName(ctx context.Context, runnerName string) error {
	log.Info("开始删除K8s Runner: %s", runnerName)

	// 创建Kubernetes客户端
	clientset, err := createKubernetesClient(setting.K8sConfig.Url, setting.K8sConfig.Token)
	if err != nil {
		return fmt.Errorf("创建Kubernetes客户端失败: %v", err)
	}

	// 设置namespace，与创建时保持一致

	namespace := "act-runner"

	// 删除Deployment
	err = clientset.AppsV1().Deployments(namespace).Delete(ctx, runnerName, metav1.DeleteOptions{})
	if err != nil {
		return fmt.Errorf("删除K8s Runner Deployment失败: %v", err)
	}

	log.Info("成功删除K8s Runner Deployment: %s", runnerName)
	return nil
}
func createKubernetesClient(k8sURL, token string) (*kubernetes.Clientset, error) {
	config := &rest.Config{
		Host:        k8sURL,
		BearerToken: token,
		TLSClientConfig: rest.TLSClientConfig{
			Insecure: true,
		},
	}

	// 创建客户端
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, fmt.Errorf("创建Kubernetes客户端失败: %v", err)
	}

	return clientset, nil
}
func testKubernetesConnection(k8sURL, token string) error {
	clientset, err := createKubernetesClient(k8sURL, token)
	if err != nil {
		return err
	}

	// 尝试获取节点列表来测试连接
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	_, err = clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{Limit: 1})
	if err != nil {
		return fmt.Errorf("无法连接到Kubernetes集群: %v", err)
	}

	log.Info("Kubernetes连接测试成功")
	return nil
}

func createRunnerDeployment(token, instanceURL string) (*appsv1.Deployment, error) {
	timestamp := time.Now().Format("20060102150405")
	name := "act-runner-" + timestamp

	labels := map[string]string{
		"app":     "act-runner",
		"type":    "runner",
		"version": "1.0",
	}

	// 副本数从配置获取
	replicas := int32(1)

	// 创建Deployment配置
	deployment := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:   name,
			Labels: labels,
		},
		Spec: appsv1.DeploymentSpec{
			Replicas: &replicas,
			Selector: &metav1.LabelSelector{
				MatchLabels: map[string]string{
					"app": "act-runner",
				},
			},
			Template: corev1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"app": "act-runner",
					},
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:  "act-runner", // 匹配现有配置中的容器名
							Image: setting.Runner.Image,
							Ports: []corev1.ContainerPort{
								{
									Name:          "http-0",
									ContainerPort: 3000,
									Protocol:      corev1.ProtocolTCP,
								},
							},
							Env: []corev1.EnvVar{
								{
									Name:  "GITEA_INSTANCE_URL",
									Value: instanceURL,
								},
								{
									Name:  "GITEA_RUNNER_REGISTRATION_TOKEN",
									Value: token,
								},
								{
									Name:  "GITEA_RUNNER_NAME", // 可选：如果需要设置runner名称
									Value: name,
								},
							},
							// 移除资源限制以匹配现有配置（现有配置中 resources: {}）
							Resources: corev1.ResourceRequirements{},
							// 挂载Docker socket
							VolumeMounts: []corev1.VolumeMount{
								{
									Name:      "docker-sock",
									MountPath: "/var/run/docker.sock",
								},
							},
							ImagePullPolicy: corev1.PullIfNotPresent,
						},
					},
					// Docker socket卷
					Volumes: []corev1.Volume{
						{
							Name: "docker-sock",
							VolumeSource: corev1.VolumeSource{
								HostPath: &corev1.HostPathVolumeSource{
									Path: "/var/run/docker.sock",
								},
							},
						},
					},
					RestartPolicy:      corev1.RestartPolicyAlways,
					ServiceAccountName: "default", // 匹配现有配置
					DNSPolicy:          corev1.DNSClusterFirst,
					// 添加节点选择器（如果需要）
					NodeSelector: map[string]string{
						"kubernetes.io/hostname": "node1", // 可以从配置中读取
					},
					// 添加容忍度
					Tolerations: []corev1.Toleration{
						{
							Key:               "node.kubernetes.io/not-ready",
							Operator:          corev1.TolerationOpExists,
							Effect:            corev1.TaintEffectNoExecute,
							TolerationSeconds: func() *int64 { i := int64(300); return &i }(),
						},
						{
							Key:               "node.kubernetes.io/unreachable",
							Operator:          corev1.TolerationOpExists,
							Effect:            corev1.TaintEffectNoExecute,
							TolerationSeconds: func() *int64 { i := int64(300); return &i }(),
						},
					},
				},
			},
		},
	}

	return deployment, nil
}
